﻿#region 文件信息

/*----------------------------------------------------------------
//
// 文件名称：
// 文件功能描述：
// 设计要求：
//
// 文 件 名：    TreeListView
// 创建者：      杨程
// 创建日期：	    2022/11/17 11:30:41

//----------------------------------------------------------------*/

#endregion

namespace Vampirewal.Core.WPF.Theme;

/// <summary>
///
/// </summary>
public class TreeListView : TreeView
{
    static TreeListView()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(TreeListView), new FrameworkPropertyMetadata(typeof(TreeListView)));
    }

    private ResourceDictionary res
    {
        get
        {
            return new ResourceDictionary() { Source = new Uri("pack://application:,,,/Vampirewal.Core.WPF.Theme;component/Theme.xaml", UriKind.RelativeOrAbsolute) };
        }
    }

    public TreeListView()
    {
        //构造函数
        //var TreeListViewStyle = res["TreeListViewStyle"] as Style;

        //this.Style = TreeListViewStyle;

        Columns = new GridViewColumnCollection();
        Columns.CollectionChanged += Columns_CollectionChanged;
    }

    private void Columns_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        bool? isAddEvt = null;
        if (e.Action == NotifyCollectionChangedAction.Add)
        {
            isAddEvt = true;
        }
        else if (e.Action == NotifyCollectionChangedAction.Remove)
        {
            isAddEvt = false;
        }
        if (isAddEvt.HasValue && isAddEvt.Value)
        {
            foreach (var item in e.NewItems)
            {
                if (item is TreeItemColumn tg)
                {
                    tg.Evt_ColumnVisibleChanged -= Tg_Evt_ColumnVisibleChanged;
                    tg.Evt_ColumnVisibleChanged += Tg_Evt_ColumnVisibleChanged;
                }
            }
        }
    }

    private void Tg_Evt_ColumnVisibleChanged(DependencyObject arg1, bool isVisible)
    {
        /*
         * TODO
         * 目前控制不支持中途添加列
         *
         */
        if (arg1 is TreeItemColumn tmpColumn)
        {
            //加载模板之前
            //if (_menuHeaderContainer == null)
            //    if (_initColumnVisibleState.Keys.Contains(tmpColumn.ColumnID))
            //        _initColumnVisibleState[tmpColumn.ColumnID] = isVisible;
            //    else
            //        _initColumnVisibleState.Add(tmpColumn.ColumnID, isVisible);
            //else
            //{
            //    UpdateColumnState(tmpColumn, isVisible);
            //}
        }
    }

    #region 依赖属性

    public GridViewColumnCollection Columns
    {
        get { return (GridViewColumnCollection)GetValue(ColumnsProperty); }
        set { SetValue(ColumnsProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Columns.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ColumnsProperty =
        DependencyProperty.Register("Columns", typeof(GridViewColumnCollection), typeof(TreeListView), new PropertyMetadata(null));

    public ITreeBuilder TreeBuilder
    {
        get { return (ITreeBuilder)GetValue(TreeBuilderProperty); }
        set { SetValue(TreeBuilderProperty, value); }
    }

    // Using a DependencyProperty as the backing store for TreeBuilder.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty TreeBuilderProperty =
        DependencyProperty.Register("TreeBuilder", typeof(ITreeBuilder), typeof(TreeListView), new PropertyMetadata(null, DynamicItemsChanged));

    private static void DynamicItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is TreeListView tg)
            tg.RegisterDynamicItemsChangedEvent();
    }

    internal void RegisterDynamicItemsChangedEvent()
    {
        //TreeBuilder.SetCallback(TreeBuilder_Evt_DynamicInnerElementChanged);
        //TreeBuilder.Evt_DynamicInnerElementChanged -= TreeBuilder_Evt_DynamicInnerElementChanged;
        //TreeBuilder.Evt_SelectedNode -= TreeBuilder_Evt_SelectedNode;
        //TreeBuilder.Evt_CheckNodes -= TreeBuilder_Evt_CheckNodes;

        //TreeBuilder.Evt_DynamicInnerElementChanged += TreeBuilder_Evt_DynamicInnerElementChanged;
        //TreeBuilder.Evt_SelectedNode += TreeBuilder_Evt_SelectedNode;
        //TreeBuilder.Evt_CheckNodes += TreeBuilder_Evt_CheckNodes;
        //InsertOrReplaceFirstColumn();
    }

    #region 当前选中项

    public object SelectedItemEx
    {
        get { return (object)GetValue(SelectedItemExProperty); }
        set { SetValue(SelectedItemExProperty, value); }
    }

    // Using a DependencyProperty as the backing store for SelectedItemEx.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty SelectedItemExProperty =
        DependencyProperty.Register("SelectedItemEx", typeof(object), typeof(TreeListView), new PropertyMetadata(null));

    #endregion

    #region 圆角

    //public CornerRadius TreeCornerRadius
    //{
    //    get { return (CornerRadius)GetValue(TreeCornerRadiusProperty); }
    //    set { SetValue(TreeCornerRadiusProperty, value); }
    //}

    //// Using a DependencyProperty as the backing store for TreeCornerRadius.  This enables animation, styling, binding, etc...
    //public static readonly DependencyProperty TreeCornerRadiusProperty =
    //    DependencyProperty.Register("TreeCornerRadius", typeof(CornerRadius), typeof(TreeListView), new PropertyMetadata(0));

    #endregion

    #endregion

    //这两个默认的是TreeViewItem
    protected override DependencyObject GetContainerForItemOverride()//创建或标识用于显示指定项的元素。
    {
        return new TreeListViewItem();
    }

    protected override bool IsItemItsOwnContainerOverride(object item)//确定指定项是否是（或可作为）其自己的 ItemContainer
    {
        //return item is TreeListViewItem;
        bool _isTreeLVI = item is TreeListViewItem;
        return _isTreeLVI;
    }

    /// <summary>
    /// 首列
    /// </summary>
    public FirstColumn FirstColumnInfo { get; set; }

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        //GridViewColumn
        if (FirstColumnInfo == null)
        {
            throw new NullReferenceException("FirstColumnBindingPath为必要属性，不能为null");
        }

        InsertOrReplaceFirstColumn();

        AddHandler(TreeListViewItem.SelectedEvent, new RoutedEventHandler(TreeListViewItemSelectedCallback));
    }

    private void TreeListViewItemSelectedCallback(object sender, RoutedEventArgs e)
    {
        if (e.OriginalSource is TreeListViewItem tgi)
        {
            SelectedItemEx = tgi.DataContext;
            //if (AllowPaste)
            //{
            //    if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
            //        TreeBuilder.SelectedCopyItemByCtrl(tgi.DataContext);
            //    else if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
            //        TreeBuilder.SelectedCopyItemByShift(tgi.DataContext);
            //    else
            //        TreeBuilder.ClearSelectedCopyItems(tgi.DataContext);
            //}
        }
    }

    /// <summary>
    /// 插入第一列，如果已经有第一列，则替换
    /// </summary>
    private void InsertOrReplaceFirstColumn()
    {
        //if (Columns.Count > 0 && Columns[0] is GridViewColumn)
        //    return;
        Columns.Insert(0, CreateFirstColumnOfRowIndexEx());
        //if (_menuHeaderContainer != null)
        //{
        //    var firstColumnCb = CreateCheckBoxInMenu(FirstColumnInfo.HeaderDisplayValue, Guid.Empty);
        //    firstColumnCb.IsEnabled = false;
        //    _menuHeaderContainer.Children.Insert(0, firstColumnCb);
        //}
    }

    private CheckBox _headerCb;

    /// <summary>
    /// 创建首列
    /// </summary>
    /// <returns></returns>
    private GridViewColumn CreateFirstColumnOfRowIndexEx()
    {
        GridViewColumn firstColumn = new GridViewColumn();
        firstColumn.Width = FirstColumnInfo.HeaderWidth;
        firstColumn.HeaderContainerStyle = Template.Resources["FirstHeaderStyle"] as Style;

        #region 创建带复选框的列头

        StackPanel header = new StackPanel();
        header.Orientation = Orientation.Horizontal;
        header.HorizontalAlignment = HorizontalAlignment.Center;
        if (FirstColumnInfo.HasCheckBox)
        {
            _headerCb = new CheckBox()
            {
                Margin = new Thickness(3, 0, 5, 0),
            };
            _headerCb.Click += _headerCb_Click; ;
            header.Children.Add(_headerCb);
        }
        TextBlock headerText = new TextBlock()
        {
            Text = FirstColumnInfo.HeaderDisplayValue,
        };
        header.Children.Add(headerText);
        firstColumn.Header = header;

        #endregion

        FrameworkElementFactory firstContainer = null;
        if (FirstColumnInfo is AutoCodeFirstColumn auto)
        {
            firstContainer = CreateAutoCodeCellTempalte(auto);
        }
        else if (FirstColumnInfo is CustromFirstColumn cus)
        {
            firstContainer = CreateCustromCellTempalte(cus);
        }

        firstColumn.CellTemplate = new DataTemplate() { VisualTree = firstContainer };
        return firstColumn;
    }

    private void _headerCb_Click(object sender, RoutedEventArgs e)
    {
        if (sender is CheckBox cb && cb.IsChecked.HasValue)
        {
            TreeBuilder?.SetCheckStatusOfAllNodes(cb.IsChecked.Value);
        }
    }

    /// <summary>
    /// 创建自动序号的首列
    /// </summary>
    /// <param name="autoColumn"></param>
    /// <returns></returns>
    private FrameworkElementFactory CreateAutoCodeCellTempalte(AutoCodeFirstColumn autoColumn)
    {
        FrameworkElementFactory firstContainer = new FrameworkElementFactory(typeof(DockPanel));
        firstContainer.SetValue(DockPanel.BackgroundProperty, Brushes.Transparent);

        #region 创建折叠按钮

        FrameworkElementFactory tgb = new FrameworkElementFactory(typeof(ToggleButton));
        tgb.SetValue(ToggleButton.WidthProperty, 19d);
        tgb.SetValue(ToggleButton.HeightProperty, 13d);
        tgb.SetValue(ToggleButton.StyleProperty, res["ExpandCollapseToggleStyle"] as Style);

        Binding tgbMarginBind = new Binding("Level");
        tgbMarginBind.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor);
        tgbMarginBind.RelativeSource.AncestorType = typeof(TreeViewItem);
        tgbMarginBind.Converter = new LevelToIndentConverter();
        tgb.SetBinding(ToggleButton.MarginProperty, tgbMarginBind);

        Binding checkBind = new Binding("IsExpanded");
        checkBind.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor);
        checkBind.RelativeSource.AncestorType = typeof(TreeViewItem);
        tgb.SetBinding(ToggleButton.IsCheckedProperty, checkBind);

        Binding visibleBind = new Binding("HasItems");
        visibleBind.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor);
        visibleBind.RelativeSource.AncestorType = typeof(TreeViewItem);
        visibleBind.Converter = new BoolToHideConverter();
        tgb.SetBinding(ToggleButton.VisibilityProperty, visibleBind);
        firstContainer.AppendChild(tgb);

        #endregion

        Binding headerBind = new Binding("TreeLevelPath");
        if (autoColumn.HasCheckBox)
        {
            var firstCellTemplateOfCheckBox = new FrameworkElementFactory(typeof(CheckBox));
            firstCellTemplateOfCheckBox.SetValue(CheckBox.ForegroundProperty, autoColumn.Foreground);
            firstCellTemplateOfCheckBox.SetValue(CheckBox.MarginProperty, new Thickness(10, 0, 0, 0));
            firstCellTemplateOfCheckBox.SetValue(CheckBox.VerticalContentAlignmentProperty, VerticalAlignment.Center);
            firstCellTemplateOfCheckBox.SetValue(CheckBox.VerticalAlignmentProperty, autoColumn.VerticalAlignment);
            firstCellTemplateOfCheckBox.SetValue(CheckBox.HorizontalAlignmentProperty, autoColumn.HorizontalAlignment);
            firstCellTemplateOfCheckBox.AddHandler(CheckBox.CheckedEvent, new RoutedEventHandler(Click_Checked));
            firstCellTemplateOfCheckBox.AddHandler(CheckBox.UncheckedEvent, new RoutedEventHandler(Click_Checked));
            //firstCellTemplateOfCheckBox.AddHandler(CheckBox.ClickEvent, new RoutedEventHandler(Click_Checked));
            firstCellTemplateOfCheckBox.SetBinding(CheckBox.ContentProperty, headerBind);

            #region 为CheckBox创建选择绑定

            Binding cbBind = new Binding("IsChecked");
            cbBind.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
            cbBind.Mode = BindingMode.TwoWay;
            firstCellTemplateOfCheckBox.SetBinding(CheckBox.IsCheckedProperty, cbBind);
            firstContainer.AppendChild(firstCellTemplateOfCheckBox);

            #endregion
        }
        else if (autoColumn.UseAutoCode)
        {
            var firstCellTemplate = new FrameworkElementFactory(typeof(TextBlock));
            firstCellTemplate.SetValue(TextBlock.ForegroundProperty, FirstColumnInfo.Foreground);
            firstCellTemplate.SetValue(TextBlock.MarginProperty, new Thickness(10, 0, 0, 0));
            firstCellTemplate.SetValue(TextBlock.VerticalAlignmentProperty, FirstColumnInfo.VerticalAlignment);
            firstCellTemplate.SetValue(TextBlock.HorizontalAlignmentProperty, FirstColumnInfo.HorizontalAlignment);
            firstCellTemplate.SetBinding(TextBlock.TextProperty, headerBind);
            firstContainer.AppendChild(firstCellTemplate);
        }
        return firstContainer;
    }

    /// <summary>
    /// 创建自定义首列
    /// </summary>
    /// <param name="cusColumn"></param>
    /// <returns></returns>
    private FrameworkElementFactory CreateCustromCellTempalte(CustromFirstColumn cusColumn)
    {
        FrameworkElementFactory firstContainer = new FrameworkElementFactory(typeof(DockPanel));
        firstContainer.SetValue(DockPanel.BackgroundProperty, Brushes.Transparent);
        FrameworkElementFactory firstCellTemplateOfCheckBox = null;
        FrameworkElementFactory firstCellTemplate = null;

        #region 创建折叠按钮

        FrameworkElementFactory tgb = new FrameworkElementFactory(typeof(ToggleButton));
        tgb.SetValue(ToggleButton.WidthProperty, 19d);
        tgb.SetValue(ToggleButton.HeightProperty, 13d);
        tgb.SetValue(ToggleButton.StyleProperty, res["ExpandCollapseToggleStyle"] as Style);

        Binding tgbMarginBind = new Binding("Level");
        tgbMarginBind.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor);
        tgbMarginBind.RelativeSource.AncestorType = typeof(TreeViewItem);
        tgbMarginBind.Converter = new LevelToIndentConverter();
        tgb.SetBinding(ToggleButton.MarginProperty, tgbMarginBind);

        Binding checkBind = new Binding("IsExpanded");
        checkBind.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor);
        checkBind.RelativeSource.AncestorType = typeof(TreeViewItem);
        tgb.SetBinding(ToggleButton.IsCheckedProperty, checkBind);

        Binding visibleBind = new Binding("HasItems");
        visibleBind.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor);
        visibleBind.RelativeSource.AncestorType = typeof(TreeViewItem);
        visibleBind.Converter = new BoolToHideConverter();
        tgb.SetBinding(ToggleButton.VisibilityProperty, visibleBind);
        firstContainer.AppendChild(tgb);

        #endregion

        if (cusColumn.HasCheckBox)
        {
            firstCellTemplateOfCheckBox = new FrameworkElementFactory(typeof(CheckBox));
            firstCellTemplateOfCheckBox.SetValue(CheckBox.ForegroundProperty, cusColumn.Foreground);
            firstCellTemplateOfCheckBox.SetValue(CheckBox.MarginProperty, new Thickness(10, 0, 0, 0));
            firstCellTemplateOfCheckBox.SetValue(CheckBox.VerticalContentAlignmentProperty, VerticalAlignment.Center);
            firstCellTemplateOfCheckBox.SetValue(CheckBox.VerticalAlignmentProperty, cusColumn.VerticalAlignment);
            firstCellTemplateOfCheckBox.SetValue(CheckBox.HorizontalAlignmentProperty, cusColumn.HorizontalAlignment);
            firstCellTemplateOfCheckBox.AddHandler(CheckBox.CheckedEvent, new RoutedEventHandler(Click_Checked));
            firstCellTemplateOfCheckBox.AddHandler(CheckBox.UncheckedEvent, new RoutedEventHandler(Click_Checked));
            //firstCellTemplateOfCheckBox.AddHandler(CheckBox.ClickEvent, new RoutedEventHandler(Click_Checked));
            //firstCellTemplateOfCheckBox.SetBinding(CheckBox.ContentProperty, headerBind);

            #region 为CheckBox创建选择绑定

            Binding cbBind = new Binding("IsChecked");
            cbBind.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
            cbBind.Mode = BindingMode.TwoWay;
            firstCellTemplateOfCheckBox.SetBinding(CheckBox.IsCheckedProperty, cbBind);
            firstContainer.AppendChild(firstCellTemplateOfCheckBox);

            #endregion
        }
        Binding headerBind = new Binding($"DataContext.TreeNode.{cusColumn.FirstColumnBindingPath}");
        headerBind.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor);
        headerBind.RelativeSource.AncestorType = typeof(TreeViewItem);
        headerBind.Converter = cusColumn.Converter;
        if (cusColumn.IsReadOnly)
        {
            firstCellTemplate = new FrameworkElementFactory(typeof(TextBlock));
            firstCellTemplate.SetValue(TextBlock.ForegroundProperty, cusColumn.Foreground);
            firstCellTemplate.SetValue(TextBlock.MarginProperty, new Thickness(10, 0, 0, 0));
            firstCellTemplate.SetValue(TextBlock.VerticalAlignmentProperty, cusColumn.VerticalAlignment);
            firstCellTemplate.SetValue(TextBlock.HorizontalAlignmentProperty, cusColumn.HorizontalAlignment);
            firstCellTemplate.SetBinding(TextBlock.TextProperty, headerBind);
        }
        else
        {
            headerBind.NotifyOnSourceUpdated = true;
            firstCellTemplate = new FrameworkElementFactory(typeof(TextBox));
            firstCellTemplate.SetValue(TextBox.ForegroundProperty, cusColumn.Foreground);
            firstCellTemplate.SetValue(TextBox.MarginProperty, new Thickness(10, 0, 0, 0));
            firstCellTemplate.SetValue(TextBox.VerticalAlignmentProperty, cusColumn.VerticalAlignment);
            firstCellTemplate.SetValue(TextBox.HorizontalAlignmentProperty, cusColumn.HorizontalAlignment);
            firstCellTemplate.AddHandler(TextBox.LoadedEvent, new RoutedEventHandler(FirstColumnLoadedCallback));
            firstCellTemplate.SetBinding(TextBox.TextProperty, headerBind);
            if (cusColumn.JustEditDetailItem)
            {
                Binding editBind = new Binding("HasItems");
                editBind.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor);
                editBind.RelativeSource.AncestorType = typeof(TreeListViewItem);
                firstCellTemplate.SetBinding(TextBox.IsReadOnlyProperty, editBind);
            }
        }
        firstContainer.AppendChild(firstCellTemplate);
        return firstContainer;
    }

    private void FirstColumnLoadedCallback(object sender, RoutedEventArgs e)
    {
        if (sender is TextBox tb)
        {
            tb.SourceUpdated -= Tb_SourceUpdated1;
            tb.SourceUpdated += Tb_SourceUpdated1;
        }
    }

    private void Tb_SourceUpdated1(object sender, DataTransferEventArgs e)
    {
        if (sender is TextBox tb
            && FirstColumnInfo is CustromFirstColumn ctrColumn
            && ctrColumn.FirstColumnSourceUpdateCommand != null)
        {
            var data = tb.DataContext.GetType().GetProperty("TreeNode").GetValue(tb.DataContext, null);
            ctrColumn.FirstColumnSourceUpdateCommand.Execute(data);
        }
    }

    private void Click_Checked(object sender, RoutedEventArgs e)
    {
        if (sender is CheckBox cb && cb.IsChecked.HasValue)
        {
            TreeBuilder?.CheckAndUpdateStateNode(cb.DataContext, cb.IsChecked.Value);
        }

        _headerCb.IsChecked = TreeBuilder.CheckedAllNodes;
    }
}

public class TreeListViewItem : TreeViewItem
{
    static TreeListViewItem()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(TreeListViewItem), new FrameworkPropertyMetadata(typeof(TreeListViewItem)));
    }

    /// <summary>
    /// hierarchy
    /// </summary>
    public int Level
    {
        get
        {
            if (_level == -1)
            {
                TreeListViewItem parent = ItemsControl.ItemsControlFromItemContainer(this) as TreeListViewItem;//返回拥有指定的容器元素中 ItemsControl 。
                _level = (parent != null) ? parent.Level + 1 : 0;
            }
            return _level;
        }
    }

    protected override DependencyObject GetContainerForItemOverride()
    {
        TreeListViewItem item = new TreeListViewItem();
        item.ParentNode = this;
        return item;
    }

    protected override bool IsItemItsOwnContainerOverride(object item)
    {
        //return item is TreeListViewItem;
        bool _isITV = item is TreeListViewItem;
        return _isITV;
    }

    private int _level = -1;

    #region 父节点

    /// <summary>
    /// 父节点
    /// </summary>
    internal TreeListViewItem ParentNode { get; set; }

    #endregion
}

/// <summary>
/// 树形节点列
/// </summary>
public class TreeItemColumn : GridViewColumn
{
    public event Action<DependencyObject, bool> Evt_ColumnVisibleChanged;

    public TreeItemColumn()
    {
        ColumnID = Guid.NewGuid();
    }

    /// <summary>
    /// 列的别名，用于筛选
    /// </summary>
    public string Alias { get; set; }

    /// <summary>
    /// 列的唯一标识
    /// </summary>
    internal Guid ColumnID { get; }

    #region 是否显示列

    /// <summary>
    /// 是否显示
    /// </summary>
    public bool IsVisible
    {
        get { return (bool)GetValue(IsVisibleProperty); }
        set { SetValue(IsVisibleProperty, value); }
    }

    // Using a DependencyProperty as the backing store for IsVisible.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty IsVisibleProperty =
        DependencyProperty.Register("IsVisible", typeof(bool), typeof(TreeItemColumn), new PropertyMetadata(true, VisibleChangedCallback));

    private static void VisibleChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is TreeItemColumn tg)
            tg.Evt_ColumnVisibleChanged?.Invoke(d, (bool)e.NewValue);
    }

    #endregion
}